package com.xiashitech.agent.rule.usermatch.inst;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.auto.service.AutoService;
import com.xiashitech.agent.config.StartConfig;
import com.xiashitech.agent.constants.Instrumentation;
import com.xiashitech.agent.dto.UserRequest;
import com.xiashitech.agent.instrumentation.httprequest.utils.RequestInfoUtil;
import com.xiashitech.agent.rule.usermatch.base.UserInfoMatchRule;
import com.xiashitech.agent.utils.JsonUtil;
import com.xiashitech.agent.utils.UserMatchUtil;
import com.xiashitech.interfaces.agent.dto.AgentConfigDTO;

import java.lang.reflect.Modifier;
import java.util.*;

/**
 * 获取通用request里的userid,userid直接存储获通过一层对象属性存储
 * @param <REQUEST>
 */
//@AutoService(UserInfoMatchRule.class)
public class XSHttpRequestUserMatch<REQUEST> implements UserInfoMatchRule<REQUEST>{

//    @Override
//    public boolean isSuitable(REQUEST request) {
//        Class<?> cls = request.getClass();
//        return UserInfoMatchRule.isPublic(cls) && UserInfoMatchRule.hasSuperType(cls, "javax.servlet.http.HttpServletRequest");
//    }

    @Override
    public UserRequest extractUserInfo(REQUEST request) {
        List<Class> interfaces = new ArrayList();
        UserMatchUtil.getAllInterfaces(request.getClass(),interfaces);
        if(!Modifier.isPublic(request.getClass().getModifiers()) || interfaces.size() == 0 || interfaces.stream().filter(_interface -> _interface.getName().equals("javax.servlet.http.HttpServletRequest")).count() == 0) {
            return UserRequest.builder().build();
        }

        UserRequest userRequest = UserRequest.builder().requestDate(String.valueOf(new Date().getTime())).build();
        try {
            Object userPrincipal = request.getClass().getMethod("getUserPrincipal").invoke(request);
            if (userPrincipal != null) {
                Object name = userPrincipal.getClass().getMethod("getName").invoke(userPrincipal);
                if(name != null) {
                    userRequest.setUserid(name.toString());
                }
            } else {
                Object httpSession = request.getClass().getMethod("getSession",new Class[]{boolean.class}).invoke(request,false);
                List<String> usernamekeys = AgentConfigDTO.getConfig().getUsernamekey();
                if (httpSession != null) {
                    Object sessionId = httpSession.getClass().getMethod("getId").invoke(httpSession);
                    if(sessionId != null) {
                        userRequest.setSessionId(sessionId.toString());
                    }
                    if (usernamekeys != null) {
                        usernamekeys.forEach(userkey -> {
                            try {
                                Enumeration<String> attributeNames = (Enumeration<String>)httpSession.getClass().getMethod("getAttributeNames").invoke(httpSession);
                                String realUserkey = "";
                                while (attributeNames.hasMoreElements()) {
                                    String findUserkey = attributeNames.nextElement();
                                    if (userkey.equalsIgnoreCase(findUserkey)) {
                                        realUserkey = findUserkey;
                                        break;
                                    }
                                }
                                if (!"".equals(realUserkey)) {
                                    Object userInfo = httpSession.getClass().getMethod("getAttribute",new Class[]{String.class}).invoke(httpSession,realUserkey);
                                    if (userInfo instanceof String && !JsonUtil.isJson((String) userInfo)) {
                                        userRequest.setUserid((String) userInfo);
                                    } else {
                                        ObjectNode objectNode = JsonUtil.getJsonNode(userInfo);
                                        List<String> userids = AgentConfigDTO.getConfig().getUserid();
                                        userids.forEach(userid -> {
                                            JsonNode jsonNode = objectNode.findValue(userid);
                                            if(jsonNode != null) {
                                                if(jsonNode.isValueNode()) {
                                                    userRequest.setUserid(jsonNode.asText());
                                                }
                                            }
                                        });
                                    }
                                }
                            } catch (Throwable e) {
                                // StartConfig.throwableStackPrinter(e);
                            }
                        });
                    }
                }
            }
            if((userRequest.getUserid() == null || "".equals(userRequest.getUserid()))) {
                Object userId = request.getClass().getMethod("getHeader",String.class).invoke(request, Instrumentation.USERID);
                if(userId != null) {
                    userRequest.setUserid(userId.toString());
                }
            }
        } catch (Throwable e) {
            StartConfig.throwableStackPrinter(e);
        }
        return userRequest;
    }

    public void postUserInfo(REQUEST request, UserRequest userRequest) {
        boolean inClassLoad;
        try {
            Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.http.HttpServletRequest");
            inClassLoad = true;
        } catch (Throwable e) {
            inClassLoad = false;
        }
        // 看好像是在 haier dubbo debug 中注释掉的。先打开
        Boolean httpWrapper = null;
        if(AgentConfigDTO.getConfig().getHttpWrapper() != null) {
            httpWrapper = AgentConfigDTO.getConfig().getHttpWrapper().get(StartConfig.getParamValueFromKey(StartConfig.systemId));
        }
        //提取登陆用户名
        if((httpWrapper == null || httpWrapper) && inClassLoad) {
            RequestInfoUtil.extractLoginName(request,userRequest);
        }
    }

    @Override
    public Integer getOrder() {
        return 1;
    }
}
